﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DiscoPing
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private List<Ping> pingers = new List<Ping>();

        private int result = 0;
        private int timeOut = 250;
        private List<IPDisco> ipList;

        private int ttl = 5;

        private void btnStartPolling_Click(object sender, EventArgs e)
        {

            string baseIP = "192.168.0.";
            if (!string.IsNullOrEmpty(txtIPAddress.Text))
                baseIP = txtIPAddress.Text;

            DoThePing(baseIP);
        }

        public void DoThePing(string baseIP)
        {
            ipList = new List<IPDisco>();

            //string baseIP = "10.10.0.";

            Console.WriteLine("Pinging 255 destinations of D-class in {0}*", baseIP);

            CreatePingers(255);

            PingOptions po = new PingOptions(ttl, false);
            
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            byte[] data = enc.GetBytes("abababababab");

            int cnt = 1;

            foreach (Ping p in pingers)
            {
                var ip = string.Concat(baseIP, cnt.ToString());
                p.SendAsync(ip, timeOut, data, po);
                cnt += 1;
            }

        }

        public void Ping_completed(object s, PingCompletedEventArgs e)
        {

            if (e.Reply.Status == IPStatus.Success)
            {
                Console.WriteLine(string.Concat("Active IP: ", e.Reply.Address.ToString()));
                AddResultToList(e);
                result += 1;
                lblNumFound.Text = result.ToString();
            }
            else
            {
                //Console.WriteLine(String.Concat("Non-active IP: ", e.Reply.Address.ToString()))
            }
        }
       
        private void AddResultToList(PingCompletedEventArgs e)
        {
            BackgroundWorker workerHostName = new BackgroundWorker();
            workerHostName.DoWork += new DoWorkEventHandler(workerHostName_DoWork);
            workerHostName.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerHostName_RunWorkerCompleted);

            BackgroundWorker workerGetPorts = new BackgroundWorker();
            workerGetPorts.DoWork += new DoWorkEventHandler(workerGetPorts_DoWork);
            workerGetPorts.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerGetPorts_RunWorkerCompleted);

            BackgroundWorker workerRunSNMPQueries = new BackgroundWorker();
            workerRunSNMPQueries.DoWork += new DoWorkEventHandler(workerRunSNMPQueries_DoWork);
            workerRunSNMPQueries.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerRunSNMPQueries_RunWorkerCompleted);

            IPDisco ip = new IPDisco(e.Reply.Address.ToString());
            ListViewItem item = new ListViewItem(ip.IP.ToString());
            item.Name = "IP Address";
            item.SubItems.Add(ip.HostName);
            item.SubItems.Add("");
            item.SubItems.Add("");
            item.SubItems.Add("");
            item.SubItems.Add("");
            listView1.Items.Add(item);

            // FillOpenPorts(ip);
            workerHostName.RunWorkerAsync(ip);
            workerGetPorts.RunWorkerAsync(ip);
            workerRunSNMPQueries.RunWorkerAsync(ip);
        }

        private void workerRunSNMPQueries_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IPDisco ip = e.Result as IPDisco;
            if (ip == null) return;

            for (int i = 0; i < listView1.Items.Count; i++)
            {
                if (listView1.Items[i].Text == ip.IP.ToString())
                {
                    listView1.Items[i].SubItems[3].Text = ip.SysDescr;
                    listView1.Items[i].SubItems[4].Text = ip.SysObjectID;
                }
            }
        }

        private void workerRunSNMPQueries_DoWork(object sender, DoWorkEventArgs e)
        {
            IPDisco ip = e.Argument as IPDisco;
            var walker = new Disco.MibWalkerSharp();

            try
            {
                //Name: sysDescr, Oid: 1.3.6.1.2.1.1.1
                var sysDescr = walker.StartWalken("1.3.6.1.2.1.1.1", ip.IP.ToString(), txtCommunity.Text);
                if (sysDescr.Count > 0) ip.SysDescr = sysDescr[0].Data;
            }
            catch (Exception ex)
            {
                ip.SysDescr = string.Format("SysDescr Not Discovered");
            }

            try
            {
                //Name: sysObjectID Oid: 1.3.6.1.2.1.1.2
                var sysObjectID = walker.StartWalken("1.3.6.1.2.1.1.2", ip.IP.ToString(), txtCommunity.Text);
                if (sysObjectID.Count > 0) ip.SysObjectID = sysObjectID[0].Data;
            }
            catch (Exception ex)
            {
                ip.SysObjectID = string.Format("SysObjectID Not Discovered");
            }

            e.Result = ip;
        }

        private void workerHostName_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IPDisco ip = e.Result as IPDisco;
            if (ip == null) return;

            for (int i = 0; i < listView1.Items.Count; i++)
            {
                if (listView1.Items[i].Text == ip.IP.ToString())
                {
                    listView1.Items[i].SubItems[1].Text = ip.HostName;
                }
            }
        }

        private void workerHostName_DoWork(object sender, DoWorkEventArgs e)
        {
            IPDisco ip = e.Argument as IPDisco;
            try
            {
                IPHostEntry entry = Dns.GetHostEntry(ip.IP);
                if (entry != null)
                {
                    ip.HostName = entry.HostName;
                    e.Result = ip;
                    Console.WriteLine("Host name for {0} found {1}", ip.IP.ToString(), entry.HostName);
                }
            }
            catch (SocketException)
            {
                Console.WriteLine("Host name for {0} NOT found", ip.IP.ToString());
                //unknown host or
                //not every IP has a name
                //log exception (manage it)
            }
        }

        private void workerGetPorts_DoWork(object sender, DoWorkEventArgs e)
        {

            IPDisco ip = e.Argument as IPDisco;
            ip.Ports = new List<int>();
            int[] portList = { 80, 443, 21, 22, 23 };
            List<int> openPorts = new List<int>();
            foreach (var port in portList)
            {

                try
                {
                    System.Net.Sockets.Socket sock = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
                    sock.Connect(ip.IP, port);
                    if (sock.Connected == true)
                    {
                        ip.Ports.Add(port);
                        Console.WriteLine("{0}:{1} Port is open", ip.IP.ToString(), port);
                    }
                    sock.Close();

                }
                catch (System.Net.Sockets.SocketException)
                {
                    Console.WriteLine("{0}:{1} Port is closed", ip.IP.ToString(), port);
                    //if (ex.ErrorCode == 10061) //not sure what this means.  Seems like the port is not open on the device.
                    //    Console.WriteLine("Port is Open!");
                    //else
                    //    Console.WriteLine(ex.Message);
                }
            }
            e.Result = ip;
        }

        private void workerGetPorts_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IPDisco ip = e.Result as IPDisco;
            if (ip == null) return;

            for (int i = 0; i < listView1.Items.Count; i++)
            {
                if (listView1.Items[i].Text == ip.IP.ToString())
                {
                    string ports = string.Join(",", ip.Ports.ToArray());
                    listView1.Items[i].SubItems[2].Text = ports;
                }
            }
        }

        private void CreatePingers(int cnt)
        {
            for (int i = 1; i <= cnt; i++)
            {
                Ping p = new Ping();
                p.PingCompleted += Ping_completed;
                pingers.Add(p);
            }
        }

        private void DestroyPingers()
        {
            foreach (Ping p in pingers)
            {
                p.PingCompleted -= Ping_completed;
                p.Dispose();
            }

            pingers.Clear();

        }
    }

    public class IPDisco
    {
        public IPDisco(string ipaddress)
        { this.ip = ipaddress; }

        private string ip;
        public IPAddress IP
        {
            get
            {
                return IPAddress.Parse(this.ip);
            }
            set
            {
                ip = value.ToString();
            }
        }
        public string HostName { get; set; }
        public string SysDescr { get; set; }
        public string SysObjectID { get; set; }
        public List<int> Ports { get; set; }
    }
}
